;-----------------------------------------------------------------------------
;
;	Heath-89 Overlay for ZMP (Z-Modem Program)
;       modifications by Howard Dutton
;
;	Name	ZMO-H89.Z80 
;
;	Dated Sep 14, 1988
;
;	Written by -
;	  Ron Murray, c/o Z-Node 62, 061-9-450-0200, Perth, Western Australia.
;
;	Modified to ZMP v1.2 standard rjm 15/9/88
;	Modified to ZMP v1.3 standard rjm 11/10/88
;	Modified to ZMP v1.4 standard rjm 20/11/88
;
;
;-----------------------------------------------------------------------------
;
;
;	System-dependent code overlay for ZMODEM
;
;
;
;    Insert your own code as necessary in this file. Code contained herein
; has been written in Z80 code for use with M80 or SLR. Assemble as follows:
;
;	SLR ZMO-xx01/h
;	MLOAD ZMP.COM=ZMODEM.COM,ZMO-xx01.HEX
; or
;	M80 =ZMO-xx01.Z80
;	RELHEX ZMO-xx01
;	MLOAD ZMP.COM=ZMODEM.COM,ZMO-xx01.HEX
;
;
;       (Don't use L80 without changing the source for assembly as a
;         cseg file.)
;
;-----------------------------------------------------------------------------
;
;
;    Don't forget to set your clock speed at the clkspd variable.
;
;
;    If you find your overlay exceeds the maximum size (currently 0400h),
; you will have to contact me for another version. If too many people need 
; to do it, we haven't allowed enough room.
;
; Ron Murray 15/8/88
;
;
;
;---------------------------------------------------------------------------

false	equ	0
true	equ	not false

;------------------------------------------------------------------------------

; User-set variables: 

clkspd	equ	6		; Processor clock speed in MHz
debug	equ	false		; to allow debugging of overlay with Z8E etc.

;Set the following two equates to the drive and user area which will contain
;   ZMP's .OVR files, .CFG file, .FON file and .HLP file. Set both to zero
;   (null) to locate them on the drive from which ZMP was invoked.

overdrive	equ	'A'	; Drive to find overlay files on ('A'-'P')
overuser	equ	0	; User area to find files

;------------------------------------------------------------------------------


; NOT user-set variables

userdef	equ	0145h		; origin of this overlay
				; This address should not change with
				; subsequent revisions.
mspeed	equ	03ch		; location of current baud rate. 
ovsize	equ	0400h		; max size of this overlay

	.z80			; use z80 code
	aseg			; absolute

	 if	debug
	org	100h		; so you can debug it with cebug, zsid, etc
	 else
	org	userdef
	 endif


mport	equ	0d8h		; the modem port on a h88-3 serial card
conport	equ	0e0h		; the console serial port on a H89

esc	equ	1bh
ctrlq	equ	11h
cr	equ	0dh
lf	equ	0ah
bdos	equ	5


codebgn	equ	$

;Jump table for the overlay: do NOT change this
jump_tab:
	jp	scrnpr		; screen print
	jp	mrd		; modem read with timeout
	jp	mchin		; get a character from modem
	jp	mchout		; send a character to the modem
	jp	mordy		; test for tx buffer empty
	jp	mirdy		; test for character received
	jp	sndbrk		; send break
	jp	cursadd		; cursor addressing
	jp	cls		; clear screen
	jp	invon		; inverse video on
	jp	invoff		; inverse video off
	jp	hide		; hide cursor
	jp	show		; show cursor
	jp	savecu		; save cursor position
	jp	rescu		; restore cursor position
	jp	mint		; service modem interrupt
	jp	invec		; initialise interrupt vectors
	jp	dinvec		; de-initialise interrupt vectors
	jp	mdmerr		; test uart flags for error
	jp	dtron		; turn DTR on
	jp	dtroff		; turn DTR OFF
	jp	init		; initialise uart
	jp	wait		; wait seconds
	jp	mswait		; wait milliseconds
	jp	userin		; user-defined entry routine
	jp	userout		; user-defined exit routine
	jp	getvars		; get system variables

; Spare jumps for compatibility with future versions
	jp	spare		; spare for later use
	jp	spare		; spare for later use
	jp	spare		; spare for later use
	jp	spare		; spare for later use
	jp	spare		; spare for later use
	jp	spare		; spare for later use

;
; Main code starts here
;
;Screen print function
scrnpr:
				; <== Insert your own code here
	call	print
	db	'This function not supported.',cr,lf,0
				; <== End of your own code
spare:
	ret

; User-defined entry routine: leave empty if not needed
userin:
	ret

; User-defined exit routine: leave empty if not needed
userout:
	ret


;Get a character from the modem: return in HL
mchin:
	push	bc
	in	a,(mport)
	ld	l,a		; put in HL
	ld	h,0
	or	a		; set/clear Z
	pop	bc
	ret

;Send a character to the modem
mchout:
	ld	hl,2		; get the character
	add	hl,sp
	ld	a,(hl)
	out	(mport),a
	ret			; done

;Test for output ready: return TRUE (1) in HL if ok
mordy:
	ld	hl,0		;
	in	a,(mport+5)	;
	bit	5,a		; bit 5 is set if uart is ready for a char
	jp	z,mordy1	;
	ld	hl,1

mordy1:	ld	a,l		; set/clear Z
	or	a
	ret

;Test for character at modem: return TRUE (1) in HL if so
mirdy:
	ld	hl,0		;
	in	a,(mport+5)	;
	bit	0,a		; bit 0 is set if data is ready
	jp	z,mirdy1	;
	ld	hl,1

mirdy1:	ld	a,l		; set/clear Z
	or	a
	ret

;Send a break to the modem: leave empty if your system can't do it
sndbrk:

	in	a,(mport+3)	;
	set	6,a		; set break bit
	out	(mport+3),a	;

	push	af
	ld	hl,300		; wait 300 mS
	call	waithlms
	pop	af

	res	6,a		; reset break bit
	out	(mport+3),a	;

	ret
;
;Test UART flags for error: return TRUE (1) in HL if error.
mdmerr:
	ld	hl,0
	in	a,(mport+5)	; get line status
	and	00001110B	; mask
	jp	z,mdmer1	; a should = 0 if everything is ok
	ld	hl,1		;

mdmer1:	ld	a,l		; set/clear Z
	or	a
	ret


;Turn DTR ON
dtron:
	in	a,(mport+4)	;
	set	0,a		; set the DTR bit
	out	(mport+4),a	;

	ret



;Turn DTR OFF
dtroff:
	in	a,(mport+4)	;
	res	0,a		; reset the DTR bit
	out	(mport+4),a	;

	ret


;Initialise the uart

init:	ld	hl,2		; get parameters
	add	hl,sp
	ex	de,hl
	call	getparm		; in HL
	ld	(brate),hl	; baud rate
	call	getparm
	ld	(parity),hl	; parity
	call	getparm
	ld	(data),hl	; data bits (BINARY 7 or 8)
	call	getparm
	ld	(stop),hl	; stop bits (BINARY 1 or 2)

; get the 8250 ready for programming
	ld	a,0		; clear interrupts
	out	(mport+1),a	;

	ld	a,00010000B	; set to loop-back mode
	out	(mport+4),a	;

; program the baud-rate (if it's valid)
	ld	hl,(brate)	; check to see if baud-rate is valid
	ld	de,12		;
	or	a		; clear carry
	sbc	hl,de		; hl=brate-12
	jp	p,prb1		; negitive if brate is 0..11

	ld	a,l		;
	ld	(mspeed),a	; let zmp know it's valid

	ld	a,10000000B	; set divisor latch access bit
	out	(mport+3),a	;

	ld	hl,(brate)	; compute offset to baud-rate divisor
	add	hl,hl		;
	ex	de,hl		;
	ld	hl,brtbl	;
	add	hl,de		; HL now has address of divisor
	ld	a,(hl)		; get the low-order byte
	out	(mport),a	;
	inc	hl		;
	ld	a,(hl)		;
	out	(mport+1),a	;

; program the: type of parity / # data bits / # stop bits
prb1:	ld	b,0		; set the LCR value (in B for now) to 0
	ld	a,(parity)	; set bits in B to type of parity
	cp	'N'		;
	jp	z,parN		;
	cp	'E'		;
	jp	z,ParE	 	;
	cp	'O'		;
	jp	z,ParO		;
Skip3:	ld	a,(Data)	; set bits in B to # of data bits
	cp	7		;
	jp	z,data7		;
	cp	8		;
	jp	z,data8		;
Skip4:	ld	a,(Stop)	;
	cp	1		;
	jp	z,Stop1		;
	cp	2		;
	jp	z,Stop2		;
Skip5:	ld	a,b		; get 'LCR' value
	out	(mport+3),a	; (also de-selects divisor access)

; take 8250 out of loop-back mode
	in	a,(mport)	; read a char
	add	hl,hl		; waste some time
	add	hl,hl		;
	in	a,(mport)	; read another char
	ld	a,0		;
	out	(mport+4),a	; take 8250 out of loop-back mode

	ret

; Types of parity
ParN:
	jp	Skip3
ParE:
	set	3,b
	set	4,b
	jp	Skip3
ParO:
	set	3,b
	jp	Skip3
; Number of data bits
Data7:
	set	1,b
	jp	Skip4
Data8:
	set	0,b
	set	1,b
	jp	Skip4
; Number of stop bits
Stop1:
	jp	Skip5
Stop2:	
	set	2,b
	jp	Skip5

; baud rate divisor table for 8250
brtbl:	dw	1047		; 110     0
	dw	384		; 300     1
	dw	256		; 450     2
	dw	192		; 600     3
	dw	162		; 710     4
	dw	96		; 1200    5
	dw	48		; 2400    6
	dw	24		; 4800    7
	dw	12		; 9600    8
	dw	6		; 19200   9
	dw	3		; 38400  10
	dw	2		; 57600  11

;--------------------------------------------------------------------------

stop:	dw	1		; stop bits
parity:	dw	'N'		; parity
data:	dw	8		; data bits
brate:	dw	5		; baud rate: 1200

;--------------------------------------------------------------------------
;Values of brate for each baud rate
;
; baud rate    brate
;
;   110		 0
;   300		 1
;   450		 2
;   600		 3
;   710		 4
;  1200		 5
;  2400		 6
;  4800		 7
;  9600		 8
; 19200		 9
; 38400		10
; 57600         11
;

;****************************************************************************
;Video terminal sequences: these are for the H19
;Cursor addressing:
cursadd:
	ld	hl,2		; get parameters
	add	hl,sp
	ex	de,hl
	call	getparm		; in HL
	ld	(row),hl	; row
	call	getparm
	ld	(col),hl	; column

				; using values in row and col
	call	print
	db	esc,'Y',0	; H19 leadin
	ld	a,(row)		; row first
	add	a,' '		; add offset
	call	cout
	ld	a,(col)		; sane for column
	add	a,' '
	call	cout

	ret

row:	ds	2		; row
col:	ds	2		; column


;Clear screen:
cls:
	call	print
	db	esc,'E',0
	ret

;Inverse video on:
invon:
	call	print
	db	esc,'p',0
	ret

;Inverse video off:
invoff:
	call	print
	db	esc,'q',0
	ret

;Turn off cursor:
hide:
	call	print
	db	esc,'x5',0
	ret

;Turn on cursor:
show:
	call	print
	db	esc,'y5',0
	ret

;Save cursor position:
savecu:
	call	print
	db	esc,'j',0
	ret

;Restore cursor position:
rescu:
	call	print
	db	esc,'k',0
	ret

;****************************************************************************

;Service modem interrupt:
mint:
	ret			; my system doesn't need this

;Initialise interrupt vectors:
invec:
	ret			; ditto

;De-initialise interrupt vectors:
dinvec:
	ret			; ditto

;****************** End of user-defined code ********************************
;		Do not change anything below here.

;Modem character test for 100 ms
mrd:
	push	bc		; save bc
	ld	bc,100		; set limit
mrd1:
	call	mirdy		; char at modem?
	jr	nz,mrd2		; yes, exit
	ld	hl,1		; else wait 1ms
	call	waithlms
	dec	bc		; loop till done
	ld	a,b
	or	c
	jr	nz,mrd1
	ld	hl,0		; none there, result=0
	xor	a
mrd2:
	pop	bc
	ret

; Inline print routine: destroys A and HL

print:
	ex	(sp),hl		; get address of string
ploop:
	ld	a,(hl)		; get next
	inc	hl		; bump pointer
	or	a		; done if zero
	jr	z,pdone
	call	cout		; else print
	jr	ploop		; and loop
pdone:
	ex	(sp),hl		; restore return address
	ret			; and quit

;
;Output a character in A to the console
;
cout:
	push	bc		; save regs
	push	de
	push	hl
	ld	e,a		; character to E
	ld	c,2
	call	bdos		; print it
	pop	hl
	pop	de
	pop	bc
	ret

;Wait(seconds)
wait:
	ld	hl,2
	add	hl,sp
	ex	de,hl		; get delay size
	call	getparm
				; fall thru to..
;Wait seconds in HL
waiths:
	push	bc		; save bc
	push	de		; de
	push	ix		; and ix
	ld	ix,0		; then point ix to 0
				; so we don't upset memory-mapped i/o

;Calculate values for loop constants. Need to have two loops to avoid
;   16-bit overflow with clock speeds above 9 MHz.

outerval	equ	(clkspd / 10) + 1
innerval	equ	(6667 / outerval) * clkspd

wait10:
	ld	b,outerval

wait11:
	ld	de,innerval

wait12:
	bit	0,(ix)		; time-wasters
	bit	0,(ix)
	bit	0,(ix)		; 20 T-states each
	bit	0,(ix)
	bit	0,(ix)
	bit	0,(ix)
	dec	de
	ld	a,e
	ld	a,d
	or	e
	jr	nz,wait12	; 150 T-states per inner loop
	djnz	wait11		; decrement outer loop
	dec	hl		; ok, decrement count in hl
	ld	a,h
	or	l
	jr	nz,wait10
	pop	ix		; done -- restore ix
	pop	de		; de
	pop	bc		; and bc
	ret

;Wait milliseconds
mswait:
	ld	hl,2
	add	hl,sp
	ex	de,hl		; get delay size
	call	getparm
				; fall thru to..
;Wait milliseconds in HL
waithlms:
	push	de
w1ms0:
	ld	de,39 * clkspd
w1ms1:
	dec	de
	ld	a,d
	or	e
	jr	nz,w1ms1
	dec	hl
	ld	a,h
	or	l
	jr	nz,w1ms0
	pop	de
	ret

;Get next parameter from (de) into hl
getparm:
	ex	de,hl		; get address into hl
	ld	e,(hl)		; get lo
	inc	hl
	ld	d,(hl)		; then hi
	inc	hl		; bump for next
	ex	de,hl		; result in hl, address still in de
	ret

;Get address of user-defined variables

getvars:
	ld	hl,uservars
	ret

uservars:
	dw	overdrive	; .OVR etc. drive/user
	dw	overuser


	 if	($ - codebgn) gt ovsize
toobig:	jp	errval		; Overlay too large!
	 endif

	end
